package JasPerl::Util::Enumeration;

use 5.010;
use strict;
use warnings;

use feature qw(state);

use parent qw(Exporter);

use JasPerl::Util::Exceptions;

use Scalar::Util qw(blessed looks_like_number);

our @EXPORT_OK = qw(EMPTY_ENUMERATION);

my %COERCIONS = (
    'ARRAY' => sub {
        my ($class, $from) = @_;
        JasPerl::Util::CoercionException->throw(
            "Cannot create $class from " . (ref $from || 'scalar')
        ) unless JasPerl::Util::Enumeration::List->isa($class);

        return JasPerl::Util::Enumeration::List->new(@{$from});
    },
    'HASH' => sub {
        my ($class, $from) = @_;
        JasPerl::Util::CoercionException->throw(
            "Cannot create $class from " . (ref $from || 'scalar')
        ) unless JasPerl::Util::Enumeration::List->isa($class);

        return JasPerl::Util::Enumeration::List->new(
            map { { key => $_, value => $from->{$_} } } keys %{$from}
        );
}
);

sub EMPTY_ENUMERATION {
    # a blessed "constant"
    state $const = bless { };
    return $const;
}

sub from {
    my ($class, $from) = @_;
    return $class->EMPTY_ENUMERATION unless defined $from;

    if (blessed $from) {
        return $from if $from->isa($class);
    }

    if (my $method = $COERCIONS{ref $from}) {
        return $class->$method($from);
    }

    JasPerl::Util::CoercionException->throw(
        "Cannot create $class from " . (ref $from || 'scalar')
    );
}

sub from_list {
    my ($class, @from) = @_;
    return $class->EMPTY_ENUMERATION unless @from;
    return JasPerl::Util::Enumeration::List->new(@from);
}

sub from_hash {
    my ($class, %from) = @_;
    return $class->EMPTY_ENUMERATION unless %from;
    return JasPerl::Util::Enumeration::List->new(
        map { { key => $_, value => $from{$_} } } keys %from
    );
}

sub has_next {
}

sub next {
    JasPerl::Util::NoSuchElementException->throw();
}

sub list {
    my @list = ( );
    for (my $self = shift; $self->has_next(); ) {
        push @list, $self->next();
    }
    return @list;
}

package # hide from PAUSE
    JasPerl::Util::Enumeration::List;

use parent -norequire, qw(JasPerl::Util::Enumeration);

sub new {
    my $class = shift;
    bless [ @_ ], $class;
}

sub has_next {
    return scalar @{$_[0]};
}

sub next {
    JasPerl::Util::NoSuchElementException->throw()
        unless @{$_[0]};
    return shift @{$_[0]};
}

sub list {
    my $self = shift;
    # list consumes enumeration
    my @list = @{$self};
    @{$self} = ( );
    return @list;
}

1;
